package net.randomice.emf.observables.generator;

import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import net.randomice.oawutil.Monitor;
import net.randomice.oawutil.OAWProgressMonitorWrapper;

import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.emf.mwe.core.WorkflowRunner;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;

public class GenmodelGenerateAction implements IObjectActionDelegate {

	private static final String ORG_ECLIPSE_JDT_CORE_JAVANATURE = "org.eclipse.jdt.core.javanature";
	private static final String ORG_ECLIPSE_PDE_PLUGIN_NATURE = "org.eclipse.pde.PluginNature";
	private static final String ORG_ECLIPSE_PDE_SCHEMA_BUILDER = "org.eclipse.pde.SchemaBuilder";
	private static final String ORG_ECLIPSE_PDE_MANIFEST_BUILDER = "org.eclipse.pde.ManifestBuilder";
	private static final String ORG_ECLIPSE_JDT_CORE_JAVABUILDER = "org.eclipse.jdt.core.javabuilder";

	private final class Runnable implements IRunnableWithProgress {

		public void run(IProgressMonitor monitor)
				throws InvocationTargetException, InterruptedException {
			monitor.beginTask("Initializing ...", 2100);
			IFile file = (IFile) selection.getFirstElement();
			try {
				Map<String, String> properties = new HashMap<String, String>();
				Map<String, ?> slotMap = new HashMap<String, Object>();

				// configure properties passed to the workflow
				// engine
				IProject modelProject = file.getProject();
				String name = modelProject.getName() + ".observables";
				IWorkspace workspace = ResourcesPlugin.getWorkspace();
				IWorkspaceRoot root = workspace.getRoot();
				IProject observablesProject = root.getProject(name);
				if (!observablesProject.exists()) {
					// if (observablesProject.exists()) {
					// observablesProject.delete(true, new SubProgressMonitor(
					// monitor, 25));
					// }

					IProjectDescription description = workspace
							.newProjectDescription("p");
					description.setNatureIds(new String[] {
							ORG_ECLIPSE_JDT_CORE_JAVANATURE,
							ORG_ECLIPSE_PDE_PLUGIN_NATURE });
					description.setBuildSpec(new ICommand[] {
							newCommand(description,
									ORG_ECLIPSE_JDT_CORE_JAVABUILDER),
							newCommand(description,
									ORG_ECLIPSE_PDE_MANIFEST_BUILDER),
							newCommand(description,
									ORG_ECLIPSE_PDE_SCHEMA_BUILDER) });
					observablesProject.create(description,
							new SubProgressMonitor(monitor, 25));
				}
				observablesProject.open(new SubProgressMonitor(monitor, 25));
				IJavaProject observablesJavaProject = JavaCore
						.create(observablesProject);
				{
					observablesJavaProject.open(new SubProgressMonitor(monitor,
							25));
					Set<IClasspathEntry> entries = new HashSet<IClasspathEntry>();
					// entries.addAll(Arrays.asList(observablesJavaProject
					// .getRawClasspath()));
					IFolder folder = observablesProject.getFolder("src");
					IPackageFragmentRoot fragmentRoot = observablesJavaProject
							.getPackageFragmentRoot(folder);
					entries
							.add(JavaCore
									.newSourceEntry(fragmentRoot.getPath()));

					entries.add(JavaRuntime.getDefaultJREContainerEntry());

					observablesJavaProject.setRawClasspath(entries
							.toArray(new IClasspathEntry[0]),
							new SubProgressMonitor(monitor, 25));
					if (!folder.exists())
						folder.create(false, true, null);
					// folder = observablesProject.getFolder("bin");
					// folder.create(false, true, null);
					// observablesJavaProject.setOutputLocation(folder
					// .getFullPath(), null);
				}
				String genmodelFile = toString(file);
				properties.put("model", genmodelFile);
				properties.put("project", toString(observablesProject)
						.substring(5));

				ClassLoader contextClassLoader = this.getClass()
						.getClassLoader();

				try {
					WorkflowRunner runner = new WorkflowRunner();
					URL ecoreWorkflowURL = contextClassLoader
							.getResource("/workflow/generator.oaw");
					String ecoreWorkflow = FileLocator.toFileURL(
							ecoreWorkflowURL).getFile();
					monitor.worked(25);
					if (runner.prepare(ecoreWorkflow,
							new OAWProgressMonitorWrapper(monitor), properties)) {
						runner.run(ecoreWorkflow,
								new OAWProgressMonitorWrapper(monitor),
								properties, slotMap);
						observablesProject.close(new SubProgressMonitor(
								monitor, 25));
						observablesProject.open(new SubProgressMonitor(monitor,
								25));
					} else {
						System.out.println("Some Error occoured!");
					}
				} finally {
					monitor.setTaskName("refreshing ressources ...");
					observablesProject.refreshLocal(IResource.DEPTH_INFINITE,
							new SubProgressMonitor(monitor, 100));
				}
			} catch (OperationCanceledException e) {
				e.printStackTrace();
				throw e;
			} catch (Throwable e) {
				e.printStackTrace();
				throw new InvocationTargetException(e);
			}
		}

		private ICommand newCommand(IProjectDescription description,
				String builderName) {
			ICommand command = description.newCommand();
			command.setBuilderName(builderName);
			return command;
		}

		private String toString(IResource resource)
				throws MalformedURLException {
			return resource.getLocation().toFile().getAbsoluteFile().toURI()
					.toURL().toString();
		}
	}

	private IStructuredSelection selection;

	/**
	 * Constructor for Action1.
	 */
	public GenmodelGenerateAction() {
		super();
	}

	/**
	 * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
	 */
	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
	}

	/**
	 * @see IActionDelegate#run(IAction)
	 */
	public void run(IAction action) {
		Monitor.runWithMonitor("Generating EMF Observable Wrappers ...",
				new Runnable());
	}

	/**
	 * @see IActionDelegate#selectionChanged(IAction, ISelection)
	 */
	public void selectionChanged(IAction action, ISelection selection) {
		this.selection = (IStructuredSelection) selection;
	}
}
